package com.jay.studyproject.interview;

/**
 * 消息机制，事件分发，View绘制，
 */
class  Demo1 {
    /**
     * 一、Android消息机制
     * *消息机制的流程
     * (1)应用程序启动的时候，在主线程中会默认调用了 Looper.preper()方法，初始化Looper对象并绑定到当前线程中，并在Looper内部维护一个MessageQueue
     * (2)接着调用handler.sendMessage()发送消息，会通过MessageQueue.enqueueMessage()向MessageQueue中添加一条消息
     * (3)主线程调用Looper.looper()开启循环，不断轮询消息队列，通过MessageQueue.next()取出消息
     * (4)取出的message不为空则调用msg.target.dispatchMessage()传递分发消息，目标handler收到消息后会执行handler.handlerMessage()方法处理消息
     * *常见问题
     * (1) IdleHander
     * (2) 主线程Loop无限循环为什么不会卡死
     * 答：ActivityThread的 main 方法的主要作用就是做消息循环，一旦退出消息循环，主线程运行完毕，那么你的应用也就退出了。Android是事件驱动的，
     * 在Looper.loop()中不断接收事件、处理事件，而Activity的生命周期都依靠于主线程的 Loop.loop() 来调度，所以可想而知它的存活周期和 Activity 也是一致的。
     * 当没有事件需要处理时，主线程就会阻塞；当子线程往消息队列发送消息，并且往管道文件写数据时，主线程就被唤醒。真正会卡死主线程的操作是在执行回调方法 onCreate/onStart/onResume 等操作的时间过长，
     * 导致掉帧，甚至发生ANR，looper.loop() 本身不会导致应用卡死。主线程Looper从消息队列读取消息，当读完所有消息时，主线程阻塞。子线程往消息队列发送消息，
     * 并且往管道文件写数据，主线程即被唤醒，从管道文件读取数据，主线程被唤醒只是为了读取消息，当消息读取完毕，再次睡眠。因此loop的循环并不会对CPU性能有过多的消耗。
     * (3) Handler导致的内存泄露及解决方法
     * 答：非静态内部类（包括匿名内部类），默认就会持有外部类的引用，当非静态内部类对象的生命周期比外部类对象的生命周期长时，就会导致内存泄漏。
     * 三种解决方案：
     * 1.把handler类放在单独的类文件中
     * 2.静态内部类+弱引用
     * 3.Activity不可见时，清空消息队列
     *
     *
     * 二、View事件分发机制
     * 事件分发机制的传递顺序和流程
     * (1)事件分发
     * 事件:用户通过屏幕与手机交互时，每次点击、长按、移动等都是一个事件。
     * 事件分发:某一个事件从屏幕传递到各个View，由View来消费事件或者忽略这一事件的过程。
     * (2)事件分发的对象
     * 系统把事件封装为MotionEvent对象，事件分发的过程就是MotionEvent分发的过程。
     * 事件类型：
     * 按下（ACTION_DOWN）
     * 移动（ACTION_MOVE）
     *
     *
     * 抬起（ACTION_UP）
     * 取消（ACTION_CANCEL）
     * (3)事件序列
     * 从手指按下屏幕开始，到手指离开屏幕所产生的一系列事件。
     * 同一事件序列，如果子View（ViewGroup）没有处理该事件（没有消费事件），那么后续事件就不会再传递到子View中。如果没有任何子View处理，就会交给Activity的onTouchEvent处理。
     * (4)传递层级
     * Activity→Window→DecorView→ViewGroup→View
     * 如果没有被消费，则一层层的向上传递到Activity（通过每层的onTouchEvent方法）
     *
     * *事件分发机制的总结
     *
     * *简要的谈谈Android的事件分发机制？
     * 答:当点击事件发生时，首先Activity将TouchEvent传递给Window，再从Window传递给顶层View。TouchEvent会最先到达最顶层 view 的 dispatchTouchEvent(事件的分发) ，
     * 然后由 dispatchTouchEvent 方法进行分发，如果dispatchTouchEvent返回true ，则整个事件将会被销毁，如果dispatchTouchEvent返回 false ，
     * 则交给上层view的 onTouchEvent(事件的处理)方法来开始处理这个事件，如果 onInterceptTouchEvent (事件的拦截)返回 true ，
     * 也就是拦截掉了，则交给自身的 onTouchEvent 来处理，如果 onInterceptTouchEvent 返回 false ，那么事件将继续传递给子 view ，
     * 由子 view 的 dispatchTouchEvent 再来开始这个事件的分发。如果事件传递到某一层的子 view 的 onTouchEvent 上了，且这个方法返回了 false ，
     * 那么这个事件会从这个 view 往上传递，都是 onTouchEvent 来接收，直到onTouchEvent返回true为止。而如果传递到最顶view的 onTouchEvent 也返回 false 的话，这个事件就会消失。
     *
     * *为什么View有dispatchTouchEvent方法？
     * 答：因为View可以注册很多事件的监听器，如长按、滑动、点击等，它也需要一个管理者来分发
     *
     * *简述View的事件传递机制？
     * 答：触摸事件的传递流程是从dispatchTouchEvent开始的，如果不进行人工干预，则事件将会依照View树的嵌套层次从外层向内层传递，到达最内层的View时，就由它的onTouchEvent方法处理
     * 如果事件在传递过程中，进行了人工干预，事件分发函数返回true表示自行消费，返回父类的同名方法则该事件传递给自身的onTouchEvent进行处理，
     * 返回false表示该事件会回传给父view的onTouchEvent方法进行处理，此时后面的事件都接受不到了，最后由哪个View处理，以后的所有事件都交由它来处理
     * 如果事件在传递过程中，进行了人工干预，事件处理函数，返回true和调用同名方法表示该事件被消费，返回false则表示该事件回传给父类的同名方法进行处理.
     * 事件触发是先触发onTouch，再触发onClick，如果onTouch方法返回tue，表示消费掉该事件，不在继续进行事件
     *
     * *简述ViewGroup的事件传递机制？
     * 答：触摸事件的传递顺序是由Activity到ViewGroup，再由ViewGroup递归传递给他的子View，ViewGroup通过onInterceptTouchEvent方法对事件进行拦截，如果该方法返回true，
     * 则事件不会继续往下传递给子View，如果返回false或者是调用super.onInterceptTouchEvent,则事件会继续传递给子View
     * 如果事件在传递过程中，进行了人工干预，事件分发函数返回true表示事件被自行消费，返回false，则回传给父View的onTouchEvent进行处理，此时后面的事件都接受不到了，调用同名方法则继续传递
     * 如果事件在传递过程中，进行了人工干预，事件处理函数，返回true则表示该事件被消费，返回false和调用同名方法则表示该事件回传给父类的同名方法进行处理
     *
     *
     * 三、View绘制原理
     * view绘制事件的传递顺序
     * *简单介绍一下Window，ViewRootImpl，DecorView之间的联系。
     * 答：一个Activity 包含一个Window，Window是一个抽象基类，是 Activity 和整个 View 系统交互的接口，只有一个实现子类PhoneWindow，提供了一系列窗口的方法，比如设置背景，标题等。
     * 一个PhoneWindow 对应一个DecorView 跟 一个 ViewRootImpl，DecorView 是ViewTree 里面的顶层布局，是继承于FrameLayout，包含两个子View，
     * 一个id=statusBarBackground 的 View 和 LineaLayout，LineaLayout 里面包含 title 跟content，title就是平时用的TitleBar或者ActionBar，
     * contenty也是FrameLayout，activity通过 setContent（）加载布局的时候加载到这个View上。ViewRootImpl就是建立 DecorView 和 Window 之间的联系。
     *
     * *了解ViewGroup和View承担角色的区别
     * *MeasureSpec的组成----------measure
     * *ViewGroup定位子View-------layout
     * *draw
     *
     * View的绘制过程就是从ViewRoot的performTraversals方法开始的，它经过measure、layout、draw三个过程才能最终将一个View绘制出来：
     * (1)measure用来测量View的宽和高。
     * (2)layout用来确定View在父容器中放置的位置。
     * (3)draw用来将view绘制在屏幕上。
     *
     * View的工作流程主要是指measure、layout、draw这三个流程，即测量、布局、绘制，其中measure确定View的测量宽高，layout确定View的最终宽高和上下左右的位置，draw将View绘制到屏幕上
     *
     * 四、HandlerThread的使用场景和用法？
     * HandlerThread是Android中的一个线程类，它是Thread的子类，并且内部封装了Looper和Handler，提供了更方便的消息处理和线程操作。HandlerThread常用于需要在后台执行耗时任务，并与UI线程进行交互的场景。
     * 使用HandlerThread可以实现以下功能和优势：
     * 1.后台线程执行任务：HandlerThread在后台创建一个工作线程，可以在该线程中执行耗时任务，而不会阻塞UI线程，保证了应用的响应性和流畅性。
     * 2.消息处理和线程间通信：HandlerThread内部封装了Looper和Handler，可以轻松地实现消息的发送和处理，以及线程间的通信。通过HandlerThread，
     * 可以将耗时任务的结果发送到UI线程进行更新，或者接收UI线程发送的消息进行处理。
     * 3.简化线程管理：HandlerThread将线程的创建和管理进行了封装，开发人员只需要关注业务逻辑的实现，而无需手动创建和管理线程，减少了线程管理的复杂性。
     * 使用HandlerThread时，需要注意以下几点：
     * 1.启动和停止HandlerThread：
     * 2.在需要与UI线程进行交互的情况下，可以通过HandlerThread的Handler将消息发送到UI线程：
     * 3.处理耗时任务时，可以在HandlerThread的Handler中执行，并使用Handler的sendMessage()方法发送消息进行触发。
     * 4.注意处理内存泄漏问题，及时释放HandlerThread资源，例如在Activity的onDestroy()方法中停止HandlerThread。
     *
     * 五、IntentService 的应用场景和使用方式？
     * IntentService 是一个可以在后台执行耗时任务的服务，适用于一些需要进行异步处理的场景，例如：
     * 1. 下载文件：可以使用 IntentService 在后台进行文件的下载操作，避免在主线程中执行耗时操作导致界面卡顿。
     * 2. 数据同步：在应用中需要进行数据同步操作时，可以使用 IntentService 在后台获取服务器数据并更新本地数据。
     * 3. 文件处理：例如压缩、解压缩、加密等操作，可以在 IntentService 中进行，避免占用主线程导致界面无响应。
     * 4. 上传日志：可以使用 IntentService 将应用的日志文件上传到服务器，以便开发人员进行异常分析和排查问题。
     * 5. 后台推送：使用 IntentService 处理后台推送过来的消息，更新本地数据库、发送通知等操作。
     * 使用IntentService 的方式如下：
     * 1. 创建一个继承自 IntentService 的子类，并实现其抽象方法 onHandleIntent，该方法会在后台线程中执行。
     * 2. 在需要启动 IntentService 的地方调用 startService 方法，并传递一个 Intent 对象作为参数。Intent 中可以包含一些额外的数据，用于传递给 IntentService。
     * 3. 在 onHandleIntent 方法中获取传递过来的数据，进行相应的操作。
     * 4. IntentService 会自动停止，不需要手动调用 stopSelf 方法。
     *
     * 六、AsyncTask的优点和缺点？
     * 优点：AsyncTask是一种轻量级的异步任务类，它可以在线程池中执行后台任务，然后把执行进度和最终结果传递给主线程并在主线程中更新UI。通过AsyncTask可以更方便的执行后台任务及更新UI；
     * AsyncTask提供了四个核心方法，使得使用起来很方便：
     * 1、onPreExecute(),工作在主线程，任务开始前的准备工作
     * 2、doInBackground(Params...params),在线程池执行，params表示异步任务输入的参数；此方法中会调用publishProgress()来更新任务进度，
     * publishProgress（）会调用onProgressUodate方法。doInBackground执行完毕后返回计算结果。
     * 3、onProgressUodate（Progress...values）,主线程运行，后台任务进度发生改变时调用。
     * 4、onPostExecute(Result result),在主线程执行，result为doInBackground返回值
     *
     * 缺点：
     * 1. 线程池容量不够抛出异常
     * 2. 内存泄露（所以最好任务执行时间少一点） AsyncTask提供的cancell方法只是一个修改标志的方法，并没有真正的停止任务的执行，当人无论寻到此标志时才会停止。 持有外部引用，外部销毁后任务在运行，内存不会回收
     * 3. 一个线程，一个异步任务
     *
     *
     * 七、谈谈你对Activity.runOnUiThread 的理解？
     * 一般是用来将一个runnable绑定到主线程，在runOnUiThread源码里面会判断当前runnable是否是主线程，如果是直接run，如果不是，通过一个默认的空构造函数handler将runnable post 到looper里面，创建构造函数handler，会默认绑定一个主线程的looper对象
     *
     *  八、子线程能否更新UI？为什么？
     * 子线程是不能直接更新UI的
     *
     * 注意这句话，是不能直接更新，不是不能更新（极端情况下可更新）
     * 绘制过程要保持同步（否则页面不流畅），而我们的主线程负责绘制ui，极端情况就是，在Activity的onResume（含）之前的生命周期中子线程都可以进行更新ui，也就是 onCreate，onStart和onResume，此时主线程的绘制还没开始。
     *
     * 九、谈谈Handler 机制和原理？
     *
     * 十、为什么在子线程中创建Handler会抛异常？
     * 主线程默认执行了looper.prepare方法，此时使用Handler就可以往相信队列中不断进行发送消息和取出消息处理，反之没有执行looper.prepare方法，就会抛出异常，这个在源码中有所体现
     *
     *
     * 十一、试从源码角度分析Handler的post和sendMessage方法的区别和应用场景？
     * handler.post和handler.sendMessage方法最后都会调用sendMessageAtTime方法进行消息的发送，但是在post方法中message是通过getPostMessage(Runnabler)这个方法获取的message，
     * 在这个方法中有这样一句代码m.callback = r ，给message的callback赋值为runnable对象，而在dispatchMessage这个方法中对消息进行分发的时候，先进行了msg.callback != null的判断，
     * 如果不为null，消息是通过handleCallback(msg);这个方法处理的，在这个方法中message.callback.run();调用的是post方法传递过来的runnable内的run方法处理消息，如果为空，
     * 再进行handler内部的callback判断mCallback != null，如果handler内的callback不为空，执行mCallback.handleMessage(msg)这个处理消息并判断返回是否为true，
     * 如果返回true，消息处理结束，如果返回false，消息交给handler的handleMessage(msg)处理。
     * 所以区别就是调用post方法的消息是在post传递的Runnable对象的run方法中处理，而调用sendMessage方法需要重写handleMessage方法或者给handler设置callback，在callback的handleMessage中处理并返回true。
     *
     * 十二、Handler中有Loop死循环，为什么没有阻塞主线程，原理是什么?
     * 可以这样简单的来理解一下，一个Thread对应一个Looper和一个MessageQueue
     * 这个MessageQueue是个一个阻塞队列，类似BlockingQueue，不同之处在于MessageQueue的阻塞方式是通过Pipe机制实现的。
     * 阻塞队列，就是当队列里没有数据时，如果调用获取队首数据的方法时，当前线程会被阻塞（相当于执行了线程的wait方法），如果队列里面有了插入了新数据，
     * 则会唤醒被阻塞的方法（相当于执行了线程的notify方法），并返回该数据。再来看MessageQueue，这里的数据指的就是是每一个消息，这个消息则是通过handler来发送的。
     * 综上所述，线程并没有一直死循环的工作，而是在没消息时被暂时挂起了，当有新消息进来的时候，就会又开始工作。
     *
     * 十三、消息机制(Handler)的流程
     * (1)应用程序启动的时候，在主线程中会默认调用了 Looper.preper()方法，初始化Looper对象并绑定到当前线程中，并在Looper内部维护一个MessageQueue
     * (2)接着调用handler.sendMessage()发送消息，会通过MessageQueue.enqueueMessage()向MessageQueue中添加一条消息
     * (3)主线程调用Looper.looper()开启循环，不断轮询消息队列，通过MessageQueue.next()取出消息
     * (4)取出的message不为空则调用msg.target.dispatchMessage()传递分发消息，目标handler收到消息后会执行handler.handlerMessage()方法处理消息
     *
     * 十四、主线程Loop无限循环为什么不会卡死
     * ActivityThread的 main 方法的主要作用就是做消息循环，一旦退出消息循环，主线程运行完毕，那么你的应用也就退出了。Android是事件驱动的，
     * 在Looper.loop()中不断接收事件、处理事件，而Activity的生命周期都依靠于主线程的 Loop.loop() 来调度，所以可想而知它的存活周期和 Activity 也是一致的。
     * 当没有事件需要处理时，主线程就会阻塞；当子线程往消息队列发送消息，并且往管道文件写数据时，主线程就被唤醒。
     * 真正会卡死主线程的操作是在执行回调方法 onCreate/onStart/onResume 等操作的时间过长，导致掉帧，甚至发生ANR，looper.loop() 本身不会导致应用卡死。
     * 主线程Looper从消息队列读取消息，当读完所有消息时，主线程阻塞。子线程往消息队列发送消息，并且往管道文件写数据，主线程即被唤醒，从管道文件读取数据，
     * 主线程被唤醒只是为了读取消息，当消息读取完毕，再次睡眠。因此loop的循环并不会对CPU性能有过多的消耗。
     *
     * 十五、Handler导致的内存泄露及解决方法
     * 非静态内部类（包括匿名内部类），默认就会持有外部类的引用，当非静态内部类对象的生命周期比外部类对象的生命周期长时，就会导致内存泄漏。
     *
     * 三种解决方案：
     * 1.把handler类放在单独的类文件中
     * 2.静态内部类+弱引用
     * 3.Activity不可见时，清空消息队列
     *
     * 十六、HandlerThread 的使用场景和用法？
     * HandlerThread是Google帮我们封装好的，可以用来执行多个耗时操作，而不需要多次开启线程，里面是采用Handler和Looper实现的。
     * handlerThread的使用场景，一般就是在子线程中使用；
     * 例如在子线程不断地获取数据更新UI的时候，就可以用到handlerThread
     * HandlerThread产生的背景：
     * ● 在需要做子线程耗时操作时，我们有可能会new Thread来进行
     * ● 频繁多次创建子线程，对性能是有影响的
     * ● 可以自己在子线程中使用looper轮询，避免多次创建线程
     * HandlerThread的特点：
     * ● HandlerThread是一个线程类，它继承了Thread
     * ● 它的内部有自己的Looper对象，可以进行loop轮询
     * ● 使用HandlerThread的looper创建的Handler在handleMessage中可以进行耗时操作，因为它是执行在子线程的
     * ● 它的优点是不会阻塞主线程，缺点是不能同时执行多任务，需要有序执行，执行效率低。
     * 用法：
     * 1. 创建HandlerThread对象，指定名字和进程优先级
     * 2. 复写onLoopPrepared，进行准备工作
     * 3. 调用start开启HandlerThread线程轮询，执行任务
     * 4. 根据应用场景在HandlerThread的handleMessage中调用UI线程的Handler，更新UI。
     *
     * 十七、试从源码角度分析Handler的post和sendMessage方法的区别和应用场景？
     * （1）post一类的方法发送的是Runnable对象，但是最后还是会被封装成Message对象，将Runnable对象赋值给Message对象中的callback字段，然后交由sendMessageAtTime()方法发送出去。
     * 在处理消息时，会在dispatchMessage()方法里首先被handleCallback(msg)方法执行，实际上就是执行Message对象里面的Runnable对象的run方法。
     * （2）sendMessage一类方法发送的消息直接是Message对象，处理消息时，在dispatchMessage里优先级会低于handleCallback(msg)方法，是通过自己重写的handleMessage(msg)方法执行。
     *
     * 十八、View的绘制流程
     * 自定义控件：
     * 1、组合控件。这种自定义控件不需要我们自己绘制，而是使用原生控件组合成的新控件。如标题栏。
     * 2、继承原有的控件。这种自定义控件在原生控件提供的方法外，可以自己添加一些方法。如制作圆角，圆形图片。
     * 3、完全自定义控件：这个View上所展现的内容全部都是我们自己绘制出来的。比如说制作水波纹进度条。。
     * View的绘制流程：OnMeasure()——>OnLayout()——>OnDraw()
     * 第一步：OnMeasure()：测量视图大小。从顶层父View到子View递归调用measure方法，measure方法又回调OnMeasure。
     * 第二步：OnLayout()：确定View位置，进行页面布局。从顶层父View向子View的递归调用view.layout方法的过程，即父View根据上一步measure子View所得到的布局大小和布局参数，将子View放在合适的位置上。
     * 第三步：OnDraw()：绘制视图。ViewRoot创建一个Canvas对象，然后调用OnDraw()。六个步骤：①、绘制视图的背景；②、保存画布的图层（Layer）；③、绘制View的内容；④、绘制View子视图，如果没有就不用；
     * ⑤、还原图层（Layer）；⑥、绘制滚动条。
     *
     * 十九、请谈谈invalidate()和postInvalidate()方法的区别和应用场景？
     * Android中实现view的更新有两组方法，一组是invalidate，另一组是postInvalidate，其中前者是在UI线程自身中使用，而后者在非UI线程中使用。
     * ● Android提供了Invalidate方法实现界面刷新，但是Invalidate不能直接在线程中调用，因为他是违背了单线程模型：Android UI操作并不是线程安全的，并且这些操作必须在UI线程中调用。
     * ● invalidate()是用来刷新View的，必须是在UI线程中进行工作。比如在修改某个view的显示时，调用invalidate()才能看到重新绘制的界面。invalidate()的调用是把之前的旧的view从主UI线程队列中pop掉。
     * 一个Android 程序默认情况下也只有一个进程，但一个进程下却可以有许多个线程。
     * ● 在这么多线程当中，把主要是负责控制UI界面的显示、更新和控件交互的线程称为UI线程，由于onCreate()方法是由UI线程执行的，所以也可以把UI线程理解为主线程。其余的线程可以理解为工作者线程。
     * ● invalidate()得在UI线程中被调动，在工作者线程中可以通过Handler来通知UI线程进行界面更新；而postInvalidate()在工作者线程中被调用。
     * 利用invalidate()刷新界面
     * 实例化一个Handler对象，并重写handleMessage方法调用invalidate()实现界面刷新;而在线程中通过sendMessage发送界面更新消息。
     * 使用postInvalidate()刷新界面
     * 使用postInvalidate则比较简单，不需要handler，直接在线程中调用postInvalidate即可。
     *
     * 二十、谈一谈自定义View和自定义ViewGroup？
     * View是所有UI组件的基础 ViewGroup同样是View的子类
     * 1.自定义View
     * 在没有现成的View，需要自己实现的时候，就使用自定义View，一般继承自View，SurfaceView或其他的View
     * 2.自定义ViewGroup
     * 自定义ViewGroup一般是利用现有的组件根据特定的布局方式来组成新的组件，大多继承自ViewGroup或各种Layout
     * 自定义View主要是实现onMeasure+onDraw
     * 自定义ViewGroup主要是实现onMeasue+onLayout
     *
     * 二十一、谈谈View.inflate和LayoutInflater.inflate的区别？
     * 一、LayoutInflater.inflate()
     * 该方法适用于所有布局填充的的场景，但使用前需要先实例化LayoutInflater对象
     * 1.获取LayoutInflater实例
     * getLayoutInflater();
     * 这个方法可以在Activity和Fragment中使用，不过在Fragment中使用时，要传入一个bundle参数
     * getSystemService();
     * 这个为Context的方法，只要有上下文就能调用
     * LayoutInflater.from();
     * 这个是LayoutInflater的静态方法，传入上下文即可
     * 2.LayoutInflater.inflate()的重载
     * 常用的是下面两种：
     * inflate(int resource,ViewGroup root，boolean attachToRoot)
     * resource：布局资源id
     * root：resource生成view对象要填入的父布局。为null，则返回的view就是布局资源；否则，需要参照第三个参数
     * attachToRoot：是否将resource生成view对象填入root，以root作为最终返回view的根布局。 false，root不为null，则提供root的LayoutParams约束resource生成的view；true，
     * root不为null，以root作为最终返回view的根布局
     * inflate(int resource,ViewGroup root)
     * resource：布局资源
     * root：resource生成view对象要填入的父布局。为null，则返回的view就是布局资源的根布局；否则，返回的view以传入的root为根布局(相当于上面的那个第三个参数为true)
     * 该方法，实际还是调用了上一个方法
     * 二、 View.inflate()
     * 这个是View类的静态方法，可直接调用，实际上还是使用了LayoutInflater，所以，它没有直接使用LayoutInflater.inflate()强大
     * 参数含义：
     * ● context：上下文
     * ● resource：布局资源
     * ● root：resource生成view对象要填入的父布局。为null，则返回resource生成view对象，否则将view填入到root中，以root作为根布局返回
     * 三、总结
     * View.inflate()是封装了LayoutInflater的inflate()方法，由于是静态方法，比较简便；但LayoutInflater相当于比View多了一个三个参数的inflate()方法，功能更强大
     */
}
